/*!
    \copyright  Copyright (c) 2020-2025 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    le_audio_volume
    \brief
*/

#include "le_audio_volume.h"
#include "le_audio_volume_observer.h"
#include "le_audio_volume_private.h"
#include "le_audio_volume_sync.h"

#include "audio_info.h"
#include "audio_sources.h"
#include "bt_device.h"
#include "device.h"
#include "device_db_serialiser.h"
#include "device_list.h"
#include "device_properties.h"
#include "gatt_connect.h"
#include "gatt.h"
#ifdef INCLUDE_LE_AUDIO_BROADCAST
#include "le_broadcast_manager.h"
#endif
#ifdef INCLUDE_LE_AUDIO_UNICAST
#include "le_unicast_manager.h"
#endif
#include "volume_messages.h"
#include "volume_mute.h"
#include "volume_renderer_role.h"
#include "volume_utils.h"
#include "voice_sources.h"
#include <focus_device.h>

#include <logging.h>

#include <panic.h>

#define LE_AUDIO_VOLUME_LOG   DEBUG_LOG

#define LE_AUDIO_VOLUME_SERIALIZATON_DELAY   D_SEC(1)

static int vcp_audio_volume = LE_AUDIO_VOLUME_DEFAULT;
static mute_state_t vcp_audio_mute_state = unmute;

static int vcp_voice_volume = LE_AUDIO_VOLUME_DEFAULT;
static mute_state_t vcp_voice_mute_state = unmute;
static uint8 vcp_volume_persisted = 0;

static volume_t leAudioVolume_GetAudioVolume(audio_source_t source);
static void leAudioVolume_SetAudioVolume(audio_source_t source, volume_t volume);
static mute_state_t leAudioVolume_GetAudioMuteState(audio_source_t source);
static void leAudioVolume_SetAudioMuteState(audio_source_t source, mute_state_t mute_state);

static volume_t leAudioVolume_GetVoiceVolume(voice_source_t source);
static void leAudioVolume_SetVoiceVolume(voice_source_t source, volume_t volume);
static mute_state_t leAudioVolume_GetVoiceMuteState(voice_source_t source);
static void leAudioVolume_SetVoiceMuteState(voice_source_t source, mute_state_t mute_state);

static void leAudioVolume_RendererVolumeChanged(volume_renderer_volume_changed_t * volume);
static void * leAudioVolume_RendererRetrieveClientConfig(gatt_cid_t cid);
static void leAudioVolume_RendererStoreClientConfig(gatt_cid_t cid, void * config, uint8 size);


static const audio_source_volume_interface_t le_audio_volume_interface =
{
    .GetVolume = leAudioVolume_GetAudioVolume,
    .SetVolume = leAudioVolume_SetAudioVolume,
    .GetMuteState = leAudioVolume_GetAudioMuteState,
    .SetMuteState = leAudioVolume_SetAudioMuteState
};

static const voice_source_volume_interface_t le_audio_voice_volume_interface =
{
    .GetVolume = leAudioVolume_GetVoiceVolume,
    .SetVolume = leAudioVolume_SetVoiceVolume,
    .GetMuteState = leAudioVolume_GetVoiceMuteState,
    .SetMuteState = leAudioVolume_SetVoiceMuteState
};

/*! \brief Read the volume for a LE audio source from persistent storage.

    The volume for all LE audio sources is stored in the
    same DEVICE_TYPE_SELF device property.

    The volume will generally only be read from the device property if the
    audio source being changed is currently connected. Note that it does not
    have to be routed by the audio router.

    \param[in] source Audio source to get the volume for.

    \return The stored volume, in VCP units, for the given source.
*/
static volume_t leAudioVolume_ReadAudioVolume(audio_source_t source)
{
    volume_t volume = LE_AUDIO_VOLUME(vcp_audio_volume);
    device_t device = BtDevice_GetSelfDevice();

    /* LE Audio volume is not AG specific rather EB device specific 
       So we will not use source to get device */
    UNUSED(source);

    if (device)
    {
        /* The per-device audio volume property is used to store the current LE
           audio volume. This is so that the end user
           will not get a change in volume when switching between A2DP and a LE
           audio source from the same handset. */
        if (DeviceProperties_GetLeVolume(device, volume.config, &volume))
        {
            vcp_volume_persisted = 0x01;
        }
    }

    DEBUG_LOG("leAudioVolume_ReadAudioVolume device 0x%x vol %d", device, volume.value);

    return volume;
}

/*! \brief Store the volume for a LE audio source to persistent storage.

    The volume for all LE audio sources is stored in the
    same DEVICE_TYPE_SELF device property.

    See leAudioVolume_ReadAudioVolume for details of how the device to store
    the volume is selected.

    \param[in] source Audio source to get the volume for.
    \param[in] volume Volume, in VCP units, to store.
*/
static void leAudioVolume_StoreAudioVolume(audio_source_t source, volume_t volume)
{
    device_t device = BtDevice_GetSelfDevice();

    /* LE Audio volume is not AG specific rather EB device specific 
       So we will not use source to get device */
    UNUSED(source);

    if (device)
    {
        DeviceProperties_SetLeVolume(device, volume);
        DeviceDbSerialiser_SerialiseDeviceLater(device, LE_AUDIO_VOLUME_SERIALIZATON_DELAY);
    }

    DEBUG_LOG("leAudioVolume_StoreAudioVolume device 0x%x vol %d", device, volume.value);

    vcp_audio_volume = volume.value;
    vcp_voice_volume = volume.value;
}

static volume_t leAudioVolume_GetAudioVolume(audio_source_t source)
{
    volume_t volume = LE_AUDIO_VOLUME(LE_AUDIO_VOLUME_MIN);

    if(LeAudioVolume_IsValidLeMusicSource(source))
    {
        volume = leAudioVolume_ReadAudioVolume(source);
    }

    return volume;
}

static void leAudioVolume_SetAudioVolume(audio_source_t source, volume_t volume)
{
    if(LeAudioVolume_IsValidLeMusicSource(source))
    {
        DEBUG_LOG("leAudioVolume_SetAudioVolume volume %d", volume.value);

        leAudioVolume_StoreAudioVolume(source, volume);
    }
}

static mute_state_t leAudioVolume_GetAudioMuteState(audio_source_t source)
{
    mute_state_t mute_state = unmute;
    if(LeAudioVolume_IsValidLeMusicSource(source))
    {
        mute_state = vcp_audio_mute_state;
    }
    return mute_state;
}

static void leAudioVolume_SetAudioMuteState(audio_source_t source, mute_state_t mute_state)
{
    if(LeAudioVolume_IsValidLeMusicSource(source))
    {
        DEBUG_LOG("leAudioVolume_SetAudioMuteState mute_state enum:mute_state_t:%d", mute_state);

        vcp_audio_mute_state = mute_state;
        vcp_voice_mute_state = mute_state;
    }
}

/*! \brief Read the volume for a LE voice source from persistent storage.

    The volume for all LE voice sources is stored in the
    same DEVICE_TYPE_SELF device property.

    The volume will generally only be read from the device property if the
    voice source is currently connected. Note that it does not have to be
    routed.

    \param[in] source Voice source to get the volume for.

    \return The stored volume, in VCP units, for the given source.
*/
static volume_t leAudioVolume_ReadVoiceVolume(voice_source_t source)
{
    volume_t volume = LE_AUDIO_VOLUME(vcp_voice_volume);
    device_t device = BtDevice_GetSelfDevice();

    /* LE voice volume is not AG specific rather EB device specific 
       So we will not use source to get device */
    UNUSED(source);

    if (device)
    {
        DeviceProperties_GetLeVolume(device, volume.config, &volume);
    }

    return volume;
}

/*! \brief Store the volume for a LE voice source to persistent storage.

    The volume for all LE voice sources is stored in the
    same DEVICE_TYPE_SELF device property as the HFP volume.

    \param[in] source Voice source to get the volume for.
    \param[in] volume Volume, in VCP units, to store.
*/
static void leAudioVolume_StoreVoiceVolume(voice_source_t source, volume_t volume)
{
    device_t device = BtDevice_GetSelfDevice();

    /* LE voice volume is not AG specific rather EB device specific 
       So we will not use source to get device */
    UNUSED(source);

    if (device)
    {
        DeviceProperties_SetLeVolume(device, volume);
    }

    vcp_voice_volume = volume.value;
    vcp_audio_volume = volume.value;
}

static volume_t leAudioVolume_GetVoiceVolume(voice_source_t source)
{
    volume_t volume = LE_AUDIO_VOLUME(LE_AUDIO_VOLUME_MIN);

    if (LeAudioVolume_IsValidLeVoiceSource(source))
    {
        volume = leAudioVolume_ReadVoiceVolume(source);
    }
    return volume;
}

static void leAudioVolume_SetVoiceVolume(voice_source_t source, volume_t volume)
{
    if (LeAudioVolume_IsValidLeVoiceSource(source))
    {
        DEBUG_LOG("leAudioVolume_SetVoiceVolume volume %d", volume.value);
        leAudioVolume_StoreVoiceVolume(source, volume);
    }
}

static mute_state_t leAudioVolume_GetVoiceMuteState(voice_source_t source)
{
    mute_state_t mute_state = unmute;
    if(LeAudioVolume_IsValidLeVoiceSource(source))
    {
        mute_state = vcp_voice_mute_state;
    }
    return mute_state;
}

static void leAudioVolume_SetVoiceMuteState(voice_source_t source, mute_state_t mute_state)
{
    if(LeAudioVolume_IsValidLeVoiceSource(source))
    {
        DEBUG_LOG("leAudioVolume_SetVoiceMuteState mute_state enum:mute_state_t:%d", mute_state);

        vcp_voice_mute_state = mute_state;
        vcp_audio_mute_state = mute_state;
    }
}


DISABLE_WARNING_UNUSED()
static const audio_source_volume_interface_t * LeAudioVolume_GetAudioSourceVolumeInterface(void)
{
    return &le_audio_volume_interface;
}

DISABLE_WARNING_UNUSED()
static const voice_source_volume_interface_t * LeAudioVolume_GetVoiceSourceVolumeInterface(void)
{
    return &le_audio_voice_volume_interface;
}

static const volume_renderer_callback_interface_t volume_renderer_interface =
{
    .VolumeRenderer_VolumeChanged = leAudioVolume_RendererVolumeChanged,
    .VolumeRenderer_RetrieveClientConfig = leAudioVolume_RendererRetrieveClientConfig,
    .VolumeRenderer_StoreClientConfig = leAudioVolume_RendererStoreClientConfig
};

static bool leAudioVolume_GetRoutedGenericSource(generic_source_t* source)
{
    *source = AudioInfo_GetRoutedGenericSource();
    return (source->type != source_type_invalid);
}

static bool leAudioVolume_IsLeAudioOrVoiceSource(generic_source_t *source)
{
    bool is_le_source = FALSE;

    if (   source->type == source_type_audio
        && LeAudioVolume_IsValidLeMusicSource(source->u.audio))
    {
        is_le_source = TRUE;
    }
    else if (   source->type == source_type_voice
             && LeAudioVolume_IsValidLeVoiceSource(source->u.voice))
    {
        is_le_source = TRUE;
    }

    return is_le_source;
}

static bool leAudioVolume_IsOtherAudioSource(generic_source_t *source)
{
    bool is_other_source = FALSE;

    if (   source->type == source_type_audio
        && LeAudioVolume_IsValidOtherMusicSource(source->u.audio))
    {
        is_other_source = TRUE;
    }

    return is_other_source;
}

static void leAudioVolume_StoreVolumeForDevice(device_t device, volume_renderer_volume_changed_t * volume)
{
    /* Convert the VCP volume to the range of the source it is being sent to. */
    volume_t source_volume = LE_AUDIO_VOLUME(LE_AUDIO_VOLUME_MIN);;
    volume_t new_volume = LE_AUDIO_VOLUME(volume->volume_setting);

    PanicNull(device);

    DeviceProperties_GetLeVolume(device, source_volume.config, &source_volume);
    source_volume.value = VolumeUtils_ConvertToVolumeConfig(new_volume, source_volume.config);

    DeviceProperties_SetLeVolume(device, source_volume);
}

static void leAudioVolume_RendererVolumeChanged(volume_renderer_volume_changed_t * volume)
{
    device_t vcp_device = GattConnect_GetBtDevice(volume->cid);
    LE_AUDIO_VOLUME_LOG("leAudioVolume_RendererVolumeChanged  cid %d device %p",
                        volume->cid, vcp_device);
#ifdef INCLUDE_LE_AUDIO_BROADCAST

    /* Build a list of related sources for this handset, to which this volume change may be applied */
    bool is_bass_assistant = (vcp_device == LeBroadcastManager_GetDeviceForAudioSource(audio_source_le_audio_broadcast));
#else
	bool is_bass_assistant = FALSE;
#endif /*INCLUDE_LE_AUDIO_BROADCAST*/

    generic_source_t related_sources[] =
    {
        {.type = source_type_audio, .u.audio = DeviceProperties_GetLeAudioSource(vcp_device)},
        {.type = source_type_voice, .u.voice = DeviceProperties_GetLeVoiceSource(vcp_device)},
        {.type = source_type_audio, .u.audio = is_bass_assistant ? audio_source_le_audio_broadcast : audio_source_none},
        {.type = source_type_audio, .u.audio = DeviceProperties_GetAudioSource(vcp_device)}
    };

    generic_source_t routed_source = AudioInfo_GetRoutedGenericSource();
    bool volume_applied = FALSE;

    /* Check if any of those sources is routed.
     * We will need to store the volume and apply it to the routed graph. */
    for (unsigned idx = 0; GenericSource_IsValid(routed_source) && idx < ARRAY_DIM(related_sources); idx++)
    {
        if (GenericSource_IsSameAudioSource(routed_source, related_sources[idx]))
        {
            LE_AUDIO_VOLUME_LOG("leAudioVolume_RendererVolumeChanged, audio, vol %d mute %u source enum:audio_source_t:%u",
                        volume->volume_setting, volume->mute, routed_source.u.audio);

            /* Convert the VCP volume to the range of the source it is being sent to. */
            volume_t source_volume = AudioSources_GetVolume(routed_source.u.audio);
            volume_t new_volume = LE_AUDIO_VOLUME(volume->volume_setting);
            source_volume.value = VolumeUtils_ConvertToVolumeConfig(new_volume, source_volume.config);
            Volume_SendAudioSourceVolumeUpdateRequest(routed_source.u.audio, event_origin_external, source_volume.value);
            Volume_SendAudioSourceMuteRequest(routed_source.u.audio, event_origin_external, volume->mute);
            volume_applied = TRUE;
        }
        else if (GenericSource_IsSameVoiceSource(routed_source, related_sources[idx]))
        {
            LE_AUDIO_VOLUME_LOG("leAudioVolume_RendererVolumeChanged, voice, vol %d mute %u source enum:voice_source_t:%u",
                        volume->volume_setting, volume->mute, routed_source.u.voice);
#ifdef INCLUDE_LE_AUDIO_UNICAST
            /* Conversion is not needed here since there should be only LE voice sources in the list */
            PanicFalse((VoiceSource_IsLeUnicastSource(routed_source.u.voice)));
#endif /* INCLUDE_LE_AUDIO_UNICAST */
            Volume_SendVoiceSourceVolumeUpdateRequest(routed_source.u.voice, event_origin_external, volume->volume_setting);
            Volume_SendVoiceSourceMuteRequest(routed_source.u.voice, event_origin_external, volume->mute);
            volume_applied = TRUE;
        }
    }

    if (!volume_applied)
    {
        /* None of those sources are routed. We only need to store the volume,
         * so store the volume for this handset, since they all share the same volume. */

        bool volume_changed = (VolumeRenderer_GetVolume() != volume->volume_setting);
        bool mute_changed = (VolumeRenderer_GetMute() != volume->mute);
        audio_source_t fallback = DeviceProperties_GetLeAudioSource(vcp_device);


        /* Update the volume if the source is allocated for the LEA device, else just store the volume for device */
        if (fallback != audio_source_none)
        {
            Volume_SendAudioSourceVolumeUpdateRequest(fallback, event_origin_external, volume->volume_setting);
            Volume_SendAudioSourceMuteRequest(fallback, event_origin_external, volume->mute);
        }
        else
        {
            leAudioVolume_StoreVolumeForDevice(vcp_device, volume);

            if (volume_changed && mute_changed)
            {
                VolumeRenderer_SetVolumeStateFields(volume->cid, volume->volume_setting, volume->mute, volume->change_counter);
            }
            else if (mute_changed)
            {
                VolumeRenderer_SetMute(volume->mute);
            }
            else if (volume_changed)
            {
                VolumeRenderer_SetVolume(volume->volume_setting);
            }
        }

        LE_AUDIO_VOLUME_LOG("leAudioVolume_RendererVolumeChanged, not routed, vol %d, volume_changed %d, mute %u, mute_changed %u, device : %p, source enum:audio_source_t:%u",
                            volume->volume_setting, volume_changed, volume->mute, mute_changed, vcp_device, fallback);
    }
}

static void * leAudioVolume_RendererRetrieveClientConfig(gatt_cid_t cid)
{
    device_t device = GattConnect_GetBtDevice(cid);
    void * device_config = NULL;
    if (device)
    {
        size_t size;
        if (!Device_GetProperty(device, device_property_le_audio_volume_config, &device_config, &size))
        {
            device_config = NULL;
        }
    }
    return device_config;
}

static void leAudioVolume_RendererStoreClientConfig(gatt_cid_t cid, void * config, uint8 size)
{
    device_t device = GattConnect_GetBtDevice(cid);
    if (device)
    {
        Device_SetProperty(device, device_property_le_audio_volume_config, config, size);
        DeviceDbSerialiser_SerialiseDevice(device);
    }
}

static pdd_size_t leAudioVolume_GetDeviceDataLength(device_t device)
{
    void * config = NULL;
    size_t config_size = 0;

    if (Device_GetProperty(device, device_property_le_audio_volume_config, &config, &config_size) == FALSE)
    {
        config_size = 0;
    }
    return config_size;
}

static void leAudioVolume_SerialisetDeviceData(device_t device, void *buf, pdd_size_t offset)
{
    UNUSED(offset);
    void * config = NULL;
    size_t config_size = 0;

    if (Device_GetProperty(device, device_property_le_audio_volume_config, &config, &config_size))
    {
        memcpy(buf, config, config_size);
    }
}

static void leAudioVolume_DeserialisetDeviceData(device_t device, void *buf, pdd_size_t data_length, pdd_size_t offset)
{
    UNUSED(offset);
    Device_SetProperty(device, device_property_le_audio_volume_config, buf, data_length);
}

static void leAudioVolume_RegisterAsPersistentDeviceDataUser(void)
{
    DeviceDbSerialiser_RegisterPersistentDeviceDataUser(
        0x04,
        leAudioVolume_GetDeviceDataLength,
        leAudioVolume_SerialisetDeviceData,
        leAudioVolume_DeserialisetDeviceData);
}

static void leAudioVolume_GetLastSetLeAudioVolume(uint8 *volume_setting)
{
#ifdef INCLUDE_LE_AUDIO_UNICAST
    /* Check if le volume property is set */
    volume_t volume = leAudioVolume_ReadAudioVolume(audio_source_none);

    *volume_setting = volume.value;
#else
    UNUSED(volume_setting);
#endif
}

bool LeAudioVolume_Init(Task init_task)
{
    volume_renderer_init_t volume_renderer_init;
    uint32 cid = INVALID_CID;

    LE_AUDIO_VOLUME_LOG("LeAudioVolume_Init");
    UNUSED(init_task);

    volume_renderer_init.volume_setting = vcp_audio_volume;
    volume_renderer_init.mute = (vcp_audio_mute_state == mute) ? 1 : 0;
    volume_renderer_init.step_size = LE_AUDIO_VOLUME_STEP_SIZE;

    /* If there is an LE volume previously persisted, then use it to initialise */
    leAudioVolume_GetLastSetLeAudioVolume(&volume_renderer_init.volume_setting);

    VolumeRenderer_Init(&volume_renderer_init, &volume_renderer_interface);

    /* Set the Volume Setting Persisted flag if volume setting was persisted before EB reset/disconnection */
    if (vcp_volume_persisted)
    {
        VolumeRenderer_SetVolumeFlag(cid, vcp_volume_persisted);
    }

#ifdef INCLUDE_LE_AUDIO_BROADCAST
    AudioSources_RegisterVolume(audio_source_le_audio_broadcast, LeAudioVolume_GetAudioSourceVolumeInterface());
#endif

#ifdef INCLUDE_LE_AUDIO_UNICAST
    AudioSources_RegisterVolume(audio_source_le_audio_unicast_1, LeAudioVolume_GetAudioSourceVolumeInterface());
    AudioSources_RegisterVolume(audio_source_le_audio_unicast_2, LeAudioVolume_GetAudioSourceVolumeInterface());

    VoiceSources_RegisterVolume(voice_source_le_audio_unicast_1, LeAudioVolume_GetVoiceSourceVolumeInterface());
    VoiceSources_RegisterVolume(voice_source_le_audio_unicast_2, LeAudioVolume_GetVoiceSourceVolumeInterface());
#endif



    LeAudioVolume_RegisterVolumeObservers();

    leAudioVolume_RegisterAsPersistentDeviceDataUser();

    leAudioVolumeSync_Init();

    return TRUE;
}

bool LeAudioVolume_GetRoutedSource(generic_source_t *generic_source)
{
    bool is_routed = FALSE;
    generic_source_t routed_source = {0};

    if (   leAudioVolume_GetRoutedGenericSource(&routed_source)
        && (   leAudioVolume_IsLeAudioOrVoiceSource(&routed_source)
            || leAudioVolume_IsOtherAudioSource(&routed_source)))
    {
        is_routed = TRUE;
        *generic_source = routed_source;
    }

    return is_routed;
}

int LeAudioVolume_ConvertVcpVolumeToAudioSourceRange(generic_source_t generic_source, uint8 volume_setting)
{
    int converted_volume = 0;

    if (GenericSource_IsVoice(generic_source) && (generic_source.u.voice > voice_source_none) && (generic_source.u.voice < max_voice_sources))
    {
        volume_t source_volume = VoiceSources_GetVolume(generic_source.u.voice);
        volume_t new_volume = LE_AUDIO_VOLUME(volume_setting);

        converted_volume = VolumeUtils_ConvertToVolumeConfig(new_volume, source_volume.config);
    }
    else if(GenericSource_IsAudio(generic_source) && generic_source.u.audio != audio_source_none)
    {
        volume_t source_volume = AudioSources_GetVolume(generic_source.u.audio);
        volume_t new_volume = LE_AUDIO_VOLUME(volume_setting);

        converted_volume = VolumeUtils_ConvertToVolumeConfig(new_volume, source_volume.config);
    }
    return converted_volume;
}

